#pragma once

#include <iostream>

#define U8(x) (static_cast<uint8_t>(x))
#define U16(x) (static_cast<uint16_t>(x))
#define U32(x) (static_cast<uint32_t>(x))
#define I8(x) (static_cast<int8_t>(x))
#define I16(x) (static_cast<int16_t>(x))
#define I32(x) (static_cast<int32_t>(x))

#define U8_TO_U16(x, y) U16(U16(x) << 8 | U16(y))
#define U8_TO_U32(a, b, c, d) U32((U32(a) << 24) | (U32(b) << 16) | (U32(c) << 8) | U32(d))

#define BYTEWIDTH 8 /* 字节宽度 */

#include "spdlog/async.h"
#include "spdlog/common.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"

class Logger {
private:
    std::shared_ptr<spdlog::logger> _log;

    Logger();

public:
    static Logger& get(void);

    Logger& setTag(const std::string& tag);

    template <typename... Args>
    void warn(spdlog::format_string_t<Args...> fmt, Args&&... args)
    {
        _log->warn(std::forward<spdlog::format_string_t<Args...>>(fmt), std::forward<Args>(args)...);
    }

    template <typename... Args>
    void info(spdlog::format_string_t<Args...> fmt, Args&&... args)
    {
        _log->info(std::forward<spdlog::format_string_t<Args...>>(fmt), std::forward<Args>(args)...);
    }

    template <typename... Args>
    void error(spdlog::format_string_t<Args...> fmt, Args&&... args)
    {
        _log->error(std::forward<spdlog::format_string_t<Args...>>(fmt), std::forward<Args>(args)...);
    }
};

Logger::Logger()
{
    std::vector<spdlog::sink_ptr> sinks;
#if defined(CONSOLE_LOG_ENABLE)
    sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
#endif
    sinks.push_back(std::make_shared<spdlog::sinks::daily_file_sink_mt>(LOG_FILE_NAME, 2, 30, false, 2));
    _log = std::make_shared<spdlog::logger>("logger", sinks.begin(), sinks.end());
    _log->flush_on(spdlog::level::info);
}

Logger& Logger::get(void)
{
    static Logger log;
    return log;
}

Logger& Logger::setTag(const std::string& tag)
{
    _log->set_name(tag);
    return *this;
}

// #define log(tag, ...) Logger::get().setTag(#tag).info(__VA_ARGS__);
// #define log(tag, ...) Logger::get().setTag(tag).info(__VA_ARGS__);

#define info(tag, ...) Logger::get().setTag(tag).info(__VA_ARGS__)
#define warn(tag, ...) Logger::get().setTag(tag).warn(__VA_ARGS__)
#define error(tag, ...) Logger::get().setTag(tag).error(__VA_ARGS__)

// #define info(tag, ...) std::string(tag).empty() ? (void)nullptr : Logger::get().setTag(tag).info(__VA_ARGS__)
// #define warn(tag, ...) std::string(tag).empty() ? (void)nullptr : Logger::get().setTag(tag).warn(__VA_ARGS__)
// #define error(tag, ...) std::string(tag).empty() ? (void)nullptr : Logger::get().setTag(tag).error(__VA_ARGS__)
